package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.guigu.common.result.ResultCodeEnum;
import com.atguigu.guigu.common.utils.Assert;
import com.atguigu.service.base.utils.JwtUtils;
import com.atguigu.service.base.utils.LendNoUtils;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.*;
import com.atguigu.srb.core.mapper.LendItemMapper;
import com.atguigu.srb.core.service.*;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 标的出借记录表 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2022-11-19
 */
@Service
@Slf4j
public class LendItemServiceImpl extends ServiceImpl<LendItemMapper, LendItem> implements LendItemService {
    @Resource
    UserInfoService userInfoService;
    @Resource
    UserAccountService userAccountService;
    @Resource
    LendService lendService;
    @Resource
    TransFlowService transFlowService;
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String commitInvest(String token, BigDecimal investAmount, String lendId) {
        //1、验证投资人登录状态
        Long userId = JwtUtils.getUserId(token);
        //2、判断投资金额不能<=0
        Assert.isTrue(investAmount.doubleValue()<=0 , ResultCodeEnum.USER_INVEST_LESS_ERROR);
        //2、验证投资人 账号是否正常
        UserInfo userInfo = userInfoService.checkStatus(userId);
        //账户余额是否充足
        UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, userId));
        Assert.isTrue(userAccount==null||userAccount.getAmount().doubleValue()<investAmount.doubleValue() ,
                ResultCodeEnum.NOT_SUFFICIENT_FUNDS_ERROR);
        //3、验证标的状态、是否满标
        Lend lend = lendService.getById(lendId);
        /*
        CHECK(0, "待发布"),
        INVEST_RUN(1, "募资中"),
        PAY_RUN(2, "还款中"),
        PAY_OK(3, "已结清"),
        FINISH(4, "结标"),
        CANCEL(-1, "已撤标"),
         */
        Assert.isTrue( lend.getStatus()!=1 ,
                ResultCodeEnum.LEND_INVEST_ERROR);
        //标的金额-已投金额< 本次投标金额  不允许投资
        Assert.isTrue( lend.getAmount().subtract(lend.getInvestAmount()).doubleValue()
                        <investAmount.doubleValue(),
                ResultCodeEnum.LEND_FULL_SCALE_ERROR);

        //4、本地创建投资详情保存:lend_item
        LendItem lendItem = new LendItem();
        lendItem.setLendItemNo(LendNoUtils.getLendItemNo());
        lendItem.setLendId(lend.getId());
        lendItem.setInvestUserId(userInfo.getId());
        lendItem.setInvestName(userInfo.getName());
        lendItem.setInvestAmount(investAmount);
        lendItem.setLendYearRate(lend.getLendYearRate());
        lendItem.setInvestTime(new Date());
        lendItem.setLendStartDate(lend.getLendStartDate());
        lendItem.setLendEndDate(lend.getLendEndDate());
        /*
            计算预期收益：
                投资金额*月化率*期数
         */
        BigDecimal mountRate = lend.getLendYearRate().divide(new BigDecimal("12"), 8, BigDecimal.ROUND_DOWN);
        BigDecimal exceptAmount = mountRate.multiply(investAmount).multiply(new BigDecimal(lend.getPeriod()));
        lendItem.setExpectAmount(exceptAmount);
        lendItem.setRealAmount(new BigDecimal("0"));
        lendItem.setStatus(0);
        this.save(lendItem);
        //5、构建自动提交访问hfb投资接口的表单(冻结投资人账户投资金额)
        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);
        //投资人在hfb中的账户
        params.put("voteBindCode", userInfo.getBindCode());
        //获取借款人的bindcode
        Long borrowUserId = lend.getUserId();
        UserInfo borrowUserInfo = userInfoService.getById(borrowUserId);
        //借款人在hfb中的账户
        params.put("benefitBindCode",borrowUserInfo.getBindCode());
        //投资的项目的编号：投资的是一个标的
        params.put("agentProjectCode",lend.getLendNo());
        params.put("agentProjectName",lend.getTitle());
        //本次投资的唯一的编号：
        params.put("agentBillNo",lendItem.getLendItemNo());
        params.put("voteAmt",investAmount);
        params.put("votePrizeAmt",new BigDecimal("0"));//奖励金额
        params.put("voteFeeAmt",new BigDecimal("0"));
        params.put("projectAmt",lend.getAmount());
        params.put("notifyUrl",HfbConst.INVEST_NOTIFY_URL);
        params.put("return_url",HfbConst.INVEST_RETURN_URL);
        params.put("timestamp",System.currentTimeMillis());
        String sign = RequestHelper.getSign(params);
        params.put("sign",sign);

        //保存投资投资日志
        TransFlow transFlow = new TransFlow();
        transFlow.setUserId(userInfo.getId());
        transFlow.setUserName(userInfo.getName());
        transFlow.setTransNo(lendItem.getLendItemNo());
        //（1：充值 2：提现 3：投标 4：投资回款 ...）
        transFlow.setTransType(3);
        transFlow.setTransTypeName("投标");
        //由于回电传入的投资金额为 用户投资金额+商户奖励金额，所以此处保存 它俩的和
        transFlow.setTransAmount(investAmount.add(new BigDecimal("0")));
        transFlow.setStatus(0);
        transFlowService.save(transFlow);


        return FormHelper.buildForm(HfbConst.INVEST_URL,params);
    }

    //回调 接口+回调业务
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String investNotify(HttpServletRequest request) {
        //1、获取参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, Object> map = RequestHelper.switchMap(parameterMap);
//        log.info(JSON.toJSONString(map));
        //2、验证签名
        boolean b = RequestHelper.isSignEquals(map);
        if(!b){//签名验证失败
            log.error("投资失败,签名验证失败: {}" ,JSON.toJSONString(map) );
            return "fail";
        }
        //3、验证状态码
        String resultCode = map.get("resultCode").toString();
        //获取交易流水日志： agentBillNo
        String agentBillNo = map.get("agentBillNo").toString();
        TransFlow transFlow = transFlowService.getOne(Wrappers.lambdaQuery(TransFlow.class)
                .eq(TransFlow::getTransNo, agentBillNo));
        transFlow.setMemo(JSON.toJSONString(map));
        if(!"0001".equals(resultCode)){
            log.error("投资失败,hfb业务返回失败: {}" ,JSON.toJSONString(map) );
            //更新 交易流水日志  为失败的状态
            transFlow.setStatus(-1);
            transFlowService.updateById(transFlow);
            return "fail";
        }
        //4、验证投资金额是否正确
        String voteAmt = map.get("voteAmt").toString();//等于投资金额+商户奖励金额
        BigDecimal transAmount = transFlow.getTransAmount();
        if(new BigDecimal(voteAmt).doubleValue()!=transAmount.doubleValue()){
            log.error("投资失败,投资金额异常: {}" ,JSON.toJSONString(map) );
            //更新 交易流水日志  为失败的状态
            transFlow.setStatus(-1);
            transFlowService.updateById(transFlow);
            return "fail";
        }
        //5、更新lendItem的状态为已支付
        //agentBillNo: lendItemNo
        LendItem lendItem = this.getOne(Wrappers.lambdaQuery(LendItem.class)
                .eq(LendItem::getLendItemNo, agentBillNo));
        lendItem.setStatus(1);//已支付状态
        this.updateById(lendItem);
        //6、更新lend的投资人数+已投金额
        Long lendId = lendItem.getLendId();
        Lend lend = lendService.getById(lendId);
        lend.setInvestNum(lend.getInvestNum()+1);
        lend.setInvestAmount(lend.getInvestAmount().add(transAmount));
        lendService.updateById(lend);
        //7、更新日志
        transFlow.setStatus(1);
        transFlowService.updateById(transFlow);
        //8、更新用户账户余额(冻结投资金额)
        // 账户余额-投资金额    冻结金额+投资金额
        Long investUserId = lendItem.getInvestUserId();//获取投资人id
        //查询投资人账户对象
        UserAccount userAccount = userAccountService.getOne(Wrappers.lambdaQuery(UserAccount.class)
                .eq(UserAccount::getUserId, investUserId));
        //账户只扣减自己投资的金额，不扣除商户赠送金额
        userAccount.setAmount(userAccount.getAmount().subtract(lendItem.getInvestAmount()));
        userAccount.setFreezeAmount(userAccount.getFreezeAmount().add(lendItem.getInvestAmount()));
        userAccountService.updateById(userAccount);
        return "success";
    }
}
